package org.mercworks.igamexmlparser;

import java.util.ArrayList;
import java.util.List;

import org.mercworks.igamexmlparser.dvo.IGameBone;
import org.mercworks.igamexmlparser.dvo.EulerController;
import org.mercworks.igamexmlparser.dvo.EulerX;
import org.mercworks.igamexmlparser.dvo.EulerY;
import org.mercworks.igamexmlparser.dvo.EulerZ;
import org.mercworks.igamexmlparser.dvo.Faces;
import org.mercworks.igamexmlparser.dvo.FaceVertex;
import org.mercworks.igamexmlparser.dvo.IGameModel;
import org.mercworks.igamexmlparser.dvo.Key;
import org.mercworks.igamexmlparser.dvo.Mesh;
import org.mercworks.igamexmlparser.dvo.Modifier;
import org.mercworks.igamexmlparser.dvo.Modifiers;
import org.mercworks.igamexmlparser.dvo.Node;
import org.mercworks.igamexmlparser.dvo.PositionControllerX;
import org.mercworks.igamexmlparser.dvo.PositionControllerY;
import org.mercworks.igamexmlparser.dvo.PositionControllerZ;
import org.mercworks.igamexmlparser.dvo.PositionXYZController;
import org.mercworks.igamexmlparser.dvo.Skin;
import org.mercworks.igamexmlparser.dvo.IGameVertex;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.DefaultHandler;

public class IGameXmlParserHandler extends DefaultHandler
{
	private IGameModel iGame;
	private Node node;
	private Node nodeWithParent;
	private Mesh mesh;
	private Faces faces;
	private Modifiers modifiers;
	private Modifier modifier;
	private Skin skin;
	private IGameBone bone;
	private PositionXYZController positionXYZController;
	private PositionControllerX positionControllerX;
	private PositionControllerY positionControllerY;
	private PositionControllerZ positionControllerZ;
	private Key key;
	
	private EulerController eulerController;
	private EulerX eulerX;
	private EulerY eulerY;
	private EulerZ eulerZ;
	
	private List<IGameVertex> listVertex;
	private List<Node> listNodes;
	private List<FaceVertex> listFaceVertex;
	private List<Modifier> listModifiers;
	private List<Skin> listSkins;
	private List<IGameBone> listBones;
	private List<Key> listKeys;

	
	private static final String NODE = "Node";
	private static final String NODE_TYPE = "NodeType";
	private static final String NODE_ID = "NodeID";
	private static final String MESH = "Mesh";
	private static final String VERTICES = "Vertices";
	private static final String FACE_VERTICES = "FaceVertices";
	
	private static final String MODIFIERS = "Modifiers";
	private static final String MODIFIER = "Modifier";

	private static final String SKIN = "Skin";
	private static final String SKIN_VERTEX_ID = "VertexID";
	
	private static final String BONE = "Bone";
	private static final String BONE_ID = "BoneID";
	private static final String BONE_WEIGHT = "Weight";
	
	private static final String POSITION_XYZ_CONTROLLER = "PositionXYZController";
	private static final String POSITION_CONTROLLER_X = "X";
	private static final String POSITION_CONTROLLER_Y = "Y";
	private static final String POSITION_CONTROLLER_Z = "Z";
	private static final String KEY = "Key";
	private static final String KEY_TIME = "time";
	private static final String KEY_VALUE = "value";
	
	private static final String EULER_CONTROLLER = "EulerController";
	private static final String EULER_X = "EulerX";
	private static final String EULER_Y = "EulerY";
	private static final String EULER_Z = "EulerZ";
	
	private boolean startVertexParse;
	private boolean startFaceVertexParse;
	private boolean startEulerParse;
	private boolean isChildNode;
	
	private int vertexNumberPass = 1;
	private int faceNumberPass = 1;
	private int vertexCounter = 0;
	
	public IGameXmlParserHandler()
	{
		iGame = new IGameModel();
		node = new Node();
		nodeWithParent = new Node();
		mesh = new Mesh();
		modifiers = new Modifiers();
		faces = new Faces();
		
		listVertex = new ArrayList<IGameVertex>();
		listNodes = new ArrayList<Node>();
		listFaceVertex = new ArrayList<FaceVertex>();
		listModifiers = new ArrayList<Modifier>();
		listSkins = new ArrayList<Skin>();
		listBones = new ArrayList<IGameBone>();
		listKeys = new ArrayList<Key>();
		
		startVertexParse = false;
		startFaceVertexParse = false;
		startEulerParse = false;
		isChildNode = false;
	}
	
	public void startDocument()
	{
		//not needed
	}

	public void endDocument()
	{
		iGame.setListNodes(listNodes);
	}

	public void startElement(String uri, String name, String qName,
			Attributes atts)
	{
		if(NODE.equals(qName))
		{
			String parentId = atts.getValue("ParentID");
			
			if(parentId != null)
			{
				nodeWithParent = new Node();
				nodeWithParent.setNodeId(new Integer(atts.getValue(NODE_ID)));
				nodeWithParent.setNodeParentId(new Integer(parentId));
				nodeWithParent.setNodeType(atts.getValue(NODE_TYPE));
				isChildNode = true;
			}
			else
			{
				node = new Node();
				node.setNodeId(new Integer(atts.getValue(NODE_ID)));
				node.setNodeType(atts.getValue(NODE_TYPE));
				isChildNode = false;
			}

		}
		
		if(MESH.equals(qName) && "Mesh".equals(node.getNodeType()))
			mesh = new Mesh();
		
		if(VERTICES.equals(qName) && "Mesh".equals(node.getNodeType()))
		{
			startVertexParse = true;
		}
		
		if(FACE_VERTICES.equals(qName) && "Mesh".equals(node.getNodeType()))
		{
			startFaceVertexParse = true;
		}
		
		if(MODIFIERS.equals(qName))
		{
			modifiers = new Modifiers();
			modifiers.setListModifiers(listModifiers);
		}
		
		if(MODIFIER.equals(qName))
		{
			modifier = new Modifier();
			listSkins = new ArrayList<Skin>();
		}
		
		if(SKIN.equals(qName))
		{
			skin = new Skin();
			skin.setVertexId(new Integer(atts.getValue(SKIN_VERTEX_ID)));
			listBones = new ArrayList<IGameBone>();
		}
			
		if(BONE.equals(qName))
		{
			bone = new IGameBone();
			bone.setBoneId(new Integer(atts.getValue(BONE_ID)));
			String boneWeight = atts.getValue(BONE_WEIGHT);
			
			if(boneWeight != null)
				bone.setWeight(new Double(boneWeight));
		}
		
		if(POSITION_XYZ_CONTROLLER.equals(qName))
			positionXYZController = new PositionXYZController();
		
		if(POSITION_CONTROLLER_X.equals(qName))
		{
			positionControllerX = new PositionControllerX();
			listKeys = new ArrayList<Key>();
		}
		
		if(POSITION_CONTROLLER_Y.equals(qName))
		{
			positionControllerY = new PositionControllerY();
			listKeys = new ArrayList<Key>();
		}
		
		if(POSITION_CONTROLLER_Z.equals(qName))
		{
			positionControllerZ = new PositionControllerZ();
			listKeys = new ArrayList<Key>();
		}
		
		if(KEY.equals(qName))
		{
			key = new Key();
			key.setTime(new Integer(atts.getValue(KEY_TIME)));
			key.setValue(new Double(atts.getValue(KEY_VALUE)));
		}
		
		if(EULER_CONTROLLER.equals(qName))
			eulerController = new EulerController();
		
		if(EULER_X.equals(qName))
		{
			eulerX = new EulerX();
			listKeys = new ArrayList<Key>();
			startEulerParse = true;
		}
		
		if(EULER_Y.equals(qName))
		{
			eulerY = new EulerY();
			listKeys = new ArrayList<Key>();
			startEulerParse = true;
		}
		
		if(EULER_Z.equals(qName))
		{
			eulerZ = new EulerZ();
			listKeys = new ArrayList<Key>();
			startEulerParse = true;
		}
	}

	public void endElement(String uri, String name, String qName)
	{
		if(NODE.equals(qName))
		{
			if(isChildNode)
			{
				listNodes.add(nodeWithParent);
				isChildNode = false;
			}
			else
				listNodes.add(node);
		}
		if(MESH.equals(qName) && "Mesh".equals(node.getNodeType()))
		{
			mesh.setListVertex(listVertex);
			mesh.setFaces(faces);
			node.setMesh(mesh);
		}
		if(VERTICES.equals(qName) && "Mesh".equals(node.getNodeType()))
		{
			startVertexParse = false;
		}
		if(FACE_VERTICES.equals(qName) && "Mesh".equals(node.getNodeType()))
		{
			faces.setListFaceVertex(listFaceVertex);
			startFaceVertexParse = false;
		}
		
		if(MODIFIERS.equals(qName))
			node.setModifiers(modifiers);
		
		if(MODIFIER.equals(qName))
		{
			modifier.setListSkins(listSkins);
			listModifiers.add(modifier);
		}
		
		if(SKIN.equals(qName))
		{
			skin.setListIGameBones(listBones);
			listSkins.add(skin);
		}
		
		if(BONE.equals(qName))
			listBones.add(bone);
		
		if(POSITION_XYZ_CONTROLLER.equals(qName))
		{
			if(isChildNode)
				nodeWithParent.setPositionXYZController(positionXYZController);
			else
				node.setPositionXYZController(positionXYZController);
		}
		
		if(POSITION_CONTROLLER_X.equals(qName))
		{
			positionControllerX.setListKeys(listKeys);
			positionXYZController.setPositionControllerX(positionControllerX);
		}
		
		if(POSITION_CONTROLLER_Y.equals(qName))
		{
			positionControllerY.setListKeys(listKeys);
			positionXYZController.setPositionControllerY(positionControllerY);
		}
		
		if(POSITION_CONTROLLER_Z.equals(qName))
		{
			positionControllerZ.setListKeys(listKeys);
			positionXYZController.setPositionControllerZ(positionControllerZ);
		}
		
		if(EULER_CONTROLLER.equals(qName))
		{
			if(isChildNode)
				nodeWithParent.setEulerController(eulerController);
			else
				node.setEulerController(eulerController);
		}
		
		if(EULER_X.equals(qName))
		{
			eulerX.setListKeys(listKeys);
			eulerController.setEulerX(eulerX);
			startEulerParse = false;
		}
		
		if(EULER_Y.equals(qName))
		{
			eulerY.setListKeys(listKeys);
			eulerController.setEulerY(eulerY);
			startEulerParse = false;
		}
		
		if(EULER_Z.equals(qName))
		{
			eulerZ.setListKeys(listKeys);
			eulerController.setEulerZ(eulerZ);
			startEulerParse = false;
		}
		
		if(KEY.equals(qName))
			listKeys.add(key);
	}

	public void characters(char ch[], int start, int length)
	{
		StringBuffer sbNumber = new StringBuffer();
		
		if(startVertexParse && "Mesh".equals(node.getNodeType()))
		{
			IGameVertex parsedVertex = null;
			
			for(int i = start; i <= length; i ++)
			{
				if(Character.isDigit(ch[i]) || ch[i] == '-' || ch[i] == '.')
					sbNumber.append(ch[i]);
				else
				{
					if(sbNumber.length() > 0)
					{
						switch(vertexNumberPass)
						{
							case 1:
								parsedVertex = new IGameVertex();
								parsedVertex.setX(new Double(sbNumber.toString()));
								break;
								
							case 2:
								parsedVertex.setY(new Double(sbNumber.toString()));
								break;
								
							case 3:
								parsedVertex.setZ(new Double(sbNumber.toString()));
								parsedVertex.setVertexId(new Integer(vertexCounter));
								listVertex.add(parsedVertex);
								vertexCounter++;
								break;
						}
						
						vertexNumberPass++;
						if(vertexNumberPass > 3)
							vertexNumberPass = 1;
						
						sbNumber.delete(0, sbNumber.length());
					}
				}
			}
		}
		
		if(startFaceVertexParse && "Mesh".equals(node.getNodeType()))
		{
			FaceVertex parsedFaceVertex = new FaceVertex();
			
			for(int i = start; i <= length; i ++)
			{
				if(Character.isDigit(ch[i]))
					sbNumber.append(ch[i]);
				else
				{
					if(sbNumber.length() > 0)
					{
						switch(faceNumberPass)
						{
							case 1:
								parsedFaceVertex = new FaceVertex();
								parsedFaceVertex.setFaceX(new Integer(sbNumber.toString()));
								break;
								
							case 2:
								parsedFaceVertex.setFaceY(new Integer(sbNumber.toString()));
								break;
								
							case 3:
								parsedFaceVertex.setFaceZ(new Integer(sbNumber.toString()));
								listFaceVertex.add(parsedFaceVertex);
								vertexCounter++;
								break;
						}
						
						faceNumberPass++;
						if(faceNumberPass > 3)
							faceNumberPass = 1;
						
						sbNumber.delete(0, sbNumber.length());
					}
				}
			}
		}
		
		if(startEulerParse)
		{
			boolean isKeyTime = false;
			boolean isKeyValue = false;
			boolean isPostProcess = false;
			Key key = new Key();
			
			for(int i = start; i <= length; i++)
			{
				if(Character.isDigit(ch[i]) && !isKeyValue)
				{
					sbNumber.append(ch[i]);
					isKeyTime = true;
				}
				else if(Character.isDigit(ch[i]) || ch[i] == '-' || ch[i] == '.' && isKeyValue)
				{
					sbNumber.append(ch[i]);
					if(i == length)
					{
						isPostProcess = true;
						break;
					}
				}
				else
				{
					if(isKeyValue == true)
					{
						key.setValue(new Double(sbNumber.toString()));
						listKeys.add(key);
						isKeyValue = false;
					}
					
					if(isKeyTime == true)
					{
						key = new Key();
						key.setTime(new Integer(sbNumber.toString()));
						isKeyTime = false;
						isKeyValue = true;
					}
					
					if(sbNumber.length() > 0)
						sbNumber.delete(0, sbNumber.length());
				}
			}
			
			if(isPostProcess)
			{
				key.setValue(new Double(sbNumber.toString()));
				listKeys.add(key);
			}
		}
	}

	public IGameModel getIGame()
	{
		return this.iGame;
	}
}
